#include "forest.h"
#include "forest_compiler.h"
#include "forest_interpreter.h"

Forest::Forest() : input_file_name_(DEFAULT_OUTPUT_FILE)
{}

Forest::Forest(string input_name) : input_file_name_(input_name), output_file_name_(DEFAULT_OUTPUT_FILE)
{}

Forest::Forest(string input_name, string output_name) : input_file_name_(input_name),
output_file_name_(output_name)
{}

void Forest::SetInputFileName(string input_name)
{
    input_file_name_ = input_name;
}

void Forest::SetOutputFileName(string output_name)
{
    output_file_name_ = output_name;
}

void Forest::SetWorkMode(FOREST_WORK_MODE mode)
{
    work_mode_ = mode;
}

void PrintHelpInfo()
{
    ostringstream oss;
    oss << "Usage:" << endl;
    oss << "forest <OPTIONS> INPUT_FILE" << endl;
    oss << "Option list:" << endl;
    oss << OPTION_HELP << "  print help info of this vm" << endl;
    oss << OPTION_GET_HELP << "  same as -h" << endl;
    oss << OPTION_INTERPRET << "  interpret the input bytecode file, default option" << endl;
    oss << OPTION_COMPILE << "  compile input file to be bytcode file, default output file is out.wd" << endl;
    oss << OPTION_OUTPUT << "  point the output file name of compiled bytecode" << endl;
    cout << oss.str() << endl;
}

inline bool IsNoneInput(int arg_num)
{
    return arg_num == 1;
}

inline bool IsForHelp(int arg_num, char* argv[])
{
    return (arg_num == 2) &&
        (strcmp(argv[1], OPTION_HELP.c_str()) == 0 || strcmp(argv[1], OPTION_GET_HELP.c_str()) == 0);
}

bool ParseOption(Options& options, int arg_num, char* argv[])
{
    if (arg_num == 2) {
        if (argv[1][0] == '-') {
            return false;
        }
        options.SetWorkMode(FOREST_WORK_MODE::INTERPRETER);
        options.SetInputFileName(DEFAULT_OUTPUT_FILE);
        return true;
    }
    int i = 1;
    bool is_output_set = false;
    while(i < arg_num) {
        if (strcmp(argv[i], OPTION_INTERPRET.c_str()) == 0) { // work as interpreter
            ++i;
            if (i < arg_num) {
                options.SetWorkMode(FOREST_WORK_MODE::INTERPRETER);
                options.SetInputFileName(argv[i]);
                ++i;
            } else {
                return false;
            }
        } else if (strcmp(argv[i], OPTION_COMPILE.c_str()) == 0) { // work as compiler
            ++i;
            if (i < arg_num) {
                options.SetWorkMode(FOREST_WORK_MODE::COMPILER);
                options.SetInputFileName(argv[i]);
                ++i;
            } else {
                return false;
            }
        } else if (strcmp(argv[i], OPTION_OUTPUT.c_str()) == 0) { // set output file name
            ++i;
            if (i < arg_num) {
                options.SetOutputFileName(argv[i]);
                is_output_set = true;
                ++i;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }
    if (!is_output_set) {
        options.SetOutputFileName(DEFAULT_OUTPUT_FILE); // default file name
    }
    return true;
}

int main(int argc, char* argv[]) {
    if (IsNoneInput(argc) || IsForHelp(argc, argv)) {
        PrintHelpInfo();
        return false;
    }
    Options options;
    if (!ParseOption(options, argc, argv)) {
        cout << "Unrecognized option!" << endl;
        PrintHelpInfo();
        return false;
    }
    Forest* forest;
    switch(options.GetWorkMode()) {
        case FOREST_WORK_MODE::COMPILER:
            forest = new ForestCompiler(options.GetInputFileName(), options.GetOutputFileName());
            break;
        case FOREST_WORK_MODE::INTERPRETER:
            forest = new ForestInterpreter(options.GetInputFileName());
            break;
    }
    forest->run();
    delete forest;
}